//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <_critsec.h>
#include <interlocked.h>
#include <assert.h>
#include <usyscall.h>
#include <elacfg.h>
#include <csdetect.h>
#include <elacs.h>

//#define THREAD_LOCAL_ID(stk)    (((int)stk & ~(USER_STACK_ALIGN - 1)) + 1)

inline int GetThreadLocalID(int *stk)
{
    UInt32 StackTop;
    StackTop = ((int)stk & ~(USER_STACK_ALIGN - 1)) + USER_STACK_ALIGN;
    if ((StackTop > THREAD_STACK_LIMIT) || (StackTop <= THREAD_STACK_BASE)) {
        SysGetThreadStackTop(&StackTop);
    }

    return ((int)(StackTop - USER_STACK_ALIGN)) + 1;
}

ECode _CCriticalSection::Initialize()
{
    cWaiters = 0;
    cNested = 0;
    nOwnerId = 0;
    event = NULL;
    bLocked = FALSE;
    sequenced = NULL;

//#ifndef _RELEASE
//    reserved = this;
//#endif

    return SysCreateEvent(&event);
}

void _CCriticalSection::Terminate()
{
//    assert(this == reserved);

    if (NULL == event) return;

    SysDestroyEvent(event);

    CSDETECT_DETACH_FROM_CSLIST
    event = NULL;

//#ifndef _RELEASE
//    reserved = NULL;
//#endif
}

void _CCriticalSection::Enter()
{
//    assert(this == reserved);

    int id;
    PVoid pSequenced = NULL;

    id = GetThreadLocalID(&id);

    if (InterlockedExchange((Int32 *)&bLocked, TRUE)) {
        if (nOwnerId != id) {
            pSequenced = _Elastos_SequencedContext_Leave();
            InterlockedIncrement(&cWaiters);
            while (InterlockedExchange((Int32 *)&bLocked, TRUE)) {
                SysWaitEvent(event);
            }
            InterlockedDecrement(&cWaiters);
            assert (cNested == 0);
            sequenced = pSequenced;
        }
    }
    else {
        assert (cNested == 0);
        pSequenced = _Elastos_SequencedContext_Leave();
        sequenced = pSequenced;
    }

    nOwnerId = id;
    cNested++;
#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
    INSERT_OWNERLIST
#endif
}

ECode _CCriticalSection::TryEnter(Boolean *pEntered)
{
//    assert(this == reserved);

    if (NULL == pEntered) return E_INVALID_ARGUMENT;

    int id;
    PVoid pSequenced = NULL;

    id = GetThreadLocalID(&id);

    if (InterlockedExchange((Int32 *)&bLocked, TRUE)) {
        if (nOwnerId != id) {
            *pEntered = FALSE;
            return NOERROR;
        }
    }
    else {
        assert (cNested == 0);
        pSequenced = _Elastos_SequencedContext_Leave();
        sequenced = pSequenced;
    }

    nOwnerId = id;
    cNested++;
#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
    INSERT_OWNERLIST
#endif
    *pEntered = TRUE;
    return NOERROR;
}

void _CCriticalSection::Leave()
{
//    assert(this == reserved);
    assert(0 < cNested);

    int id;
    id = GetThreadLocalID(&id);
    assert(id == nOwnerId);

#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
    DETACH_FROMOWNERLIST
#endif
    cNested--;
    if (0 == cNested) {
        nOwnerId = 0;
        bLocked = FALSE;
        PVoid pSequenced = sequenced;
        sequenced = NULL;
        if (cWaiters) SysNotifyEvent(event);
        _Elastos_SequencedContext_Enter(pSequenced);
    }
}
